﻿using System;
using System.Runtime.InteropServices;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.IO;
using SlimDX;

using D3DC = SlimDX.D3DCompiler;
using DX10 = SlimDX.Direct3D10;
using DXGI = SlimDX.DXGI;

namespace TriDVisualizer
{
    public class TriMesh
    {
        private DX10.Buffer m_vertexBuffer;
        private DX10.Buffer m_indexBuffer;
        private DX10.InputLayout m_vertexLayout;
        private DX10.Effect m_effect;
        private DX10.EffectTechnique m_effectTechnique;
        private DX10.EffectPass m_effectPass;
        private DX10.EffectMatrixVariable m_transformVariable;
        short[] indices;
        Vertex[] vertices;

        float valueMax;
        float valueMin;
        float UpperThreshold;
        float LowerThreshold;

        bool AutoScaleEnable;
        bool AutoThesholdEnable;
        bool SymetricColorsEnable;

        int[,] ColorMapping;

        public TriMesh()
        {
            AutoScaleEnable = true;
            AutoThesholdEnable = true;
            SymetricColorsEnable = true;
        }

        public void DisableAutoScale()
        {
            AutoScaleEnable=false;
        }

        public void PlusTheshold()
        {
            UpperThreshold = UpperThreshold * 2;
            LowerThreshold = LowerThreshold * 2;
        }
        public void MinusTheshold()
        {
            UpperThreshold = UpperThreshold / 2;
            LowerThreshold = LowerThreshold / 2;
        }
        public void UpdateValues(float [] values)
        {
            ColorMap cm = new ColorMap();
            //ColorMapping = cm.Jet();
            ColorMapping = cm.SwisstomBlue();

            updateExtremValues(values);

            if (AutoThesholdEnable)
            {
                if (UpperThreshold < valueMax)
                {
                    UpperThreshold = valueMax;
                }
                if (LowerThreshold > valueMin)
                {
                    LowerThreshold = valueMin;
                }
            }

            if (SymetricColorsEnable)
            {
                if (Math.Abs(UpperThreshold) < Math.Abs(LowerThreshold))
                {
                    UpperThreshold = Math.Abs(LowerThreshold);
                }

                if (Math.Abs(UpperThreshold) > Math.Abs(LowerThreshold))
                {
                    LowerThreshold=UpperThreshold*(float)-1.0;
                }
            }

            for (int i = 0; i < values.Length; i++)
            {
                vertices[i].Color = Value2Color(values[i]);
            }
        }

        private void updateExtremValues(float [] values)
        {
            float min = 0;
            float max = 0;
            float mean = 0;

            for (int i = 0; i < values.Length; i++)
            {
                float tempValue = values[i];
                mean = (mean + tempValue) / 2f;
                if (tempValue < min)
                    min = tempValue;
                if (tempValue > max)
                    max = tempValue;
            }
            valueMax = max;
            valueMin = min;
        }

        private uint Value2Color(float value)
        {
            float InValue = value;
            float max = 0f;
            float min = 0f;

            if (AutoScaleEnable == true)
            {
                max = valueMax;
                min = valueMin;
            }
            //else
            //{
            //    max = ColorScaleD;
            //    min = ColorScaleA;
            //}

            if (min < 0)
            {
                value = value - min;
                max = max - min;
                min = 0;

            }

            if (AutoScaleEnable == false)
            {
                if (value > UpperThreshold)
                {

                    uint alpha = (uint)ColorMapping[0, 0];
                    uint red = (uint)ColorMapping[0, 1];
                    uint green = (uint)ColorMapping[0, 2];
                    uint blue = (uint)ColorMapping[0, 3];

                    uint iCol = (alpha << 24) + (blue << 16) + (green << 8) + red;
                    return iCol;
                }
                else if (value < LowerThreshold)
                {

                    uint alpha = (uint)ColorMapping[63, 0];
                    uint red = (uint)ColorMapping[63, 1];
                    uint green = (uint)ColorMapping[63, 2];
                    uint blue = (uint)ColorMapping[63, 3];

                    uint iCol = (alpha << 24) + (blue << 16) + (green << 8) + red;
                    return iCol;
                }
                else
                {
                    float mT = 63;
                    int colorIT = (int)((InValue - LowerThreshold) * mT / (UpperThreshold - LowerThreshold));
                    if ((colorIT > mT) || (colorIT < 0))
                    {
                        colorIT = 0;
                    }
                    uint alpha = (uint)ColorMapping[colorIT, 0];
                    uint red = (uint)ColorMapping[colorIT, 1];
                    uint green = (uint)ColorMapping[colorIT, 2];
                    uint blue = (uint)ColorMapping[colorIT, 3];

                    uint iCol = (alpha << 24) + (blue << 16) + (green << 8) + red;
                    return iCol;
                }

            }
            else
            {
                float m = 63;
                int colorI = (int)((value - min) * m / (max - min));
                if ((colorI > m) || (colorI < 0))
                {
                    colorI = 0;
                }

                uint alpha = (uint)ColorMapping[colorI, 0];
                uint red = (uint)ColorMapping[colorI, 1];
                uint green = (uint)ColorMapping[colorI, 2];
                uint blue = (uint)ColorMapping[colorI, 3];

                uint iCol = (uint)(alpha << 24) + (blue << 16) + (green << 8) + red;

                return iCol;
            }
        }

        public void ResetTheshold()
        {
            UpperThreshold = 0;
            LowerThreshold = 0;
        }
        public void loadMesh(string pathIndiceFile, string pathNodeFile)
        {
            try
            {
                FileStream fs = new FileStream(pathIndiceFile, FileMode.Open);
                BinaryReader br = new BinaryReader(fs);

                int row = br.ReadInt32(); //number of row
                int col = br.ReadInt32(); //number of column

                int nbindices = row * col;

                indices = new short[nbindices];

                for (int i = 0; i < nbindices; i++)
                {
                    try
                    {
                        indices[i] = (short)br.ReadInt32();
                        indices[i]--;// file delivers 1 based indices while slimdx uses 0 based indices
                    }
                    catch (Exception e)
                    {

                    }
                }
                fs.Close();
            }
            catch (Exception e)
            {

            }

            try
            {
                FileStream fs = new FileStream(pathNodeFile, FileMode.Open);
                BinaryReader br = new BinaryReader(fs);

                int row = br.ReadInt32(); //number of row
                int col = br.ReadInt32(); //number of column
                int nbvertex = row * col;
                vertices = new Vertex[nbvertex];

                for (int i = 0; i < row; i++)
                {
                    try
                    {
                        float x = br.ReadSingle();
                        float y = br.ReadSingle();
                        float z = 0;

                        uint alpha = 255;
                        uint red = 0;
                        uint green = 0;
                        uint blue = 255;

                        uint iCol = (uint)(alpha << 24) + (blue << 16) + (green << 8) + red;
                        vertices[i] = new Vertex(new Vector3(x, y, z), iCol);

                    }
                    catch (Exception e)
                    {

                    }
                }
                fs.Close();
            }
            catch (Exception e)
            {

            }
        }
        public void UpdateResources(DX10.Device device)
        {
            DataStream outStream = new DataStream(vertices.Length * Marshal.SizeOf(typeof(Vertex)), true, true);
            DataStream outStreamIndex = new DataStream(indices.Length * Marshal.SizeOf(typeof(short)), true, true);

            for (int loop = 0; loop < vertices.Length; loop++)
            {
                outStream.Write<Vertex>(vertices[loop]);
            }

            for (int loop = 0; loop < indices.Length; loop++)
            {
                outStreamIndex.Write<short>(indices[loop]);
            }

            outStream.Position = 0;
            outStreamIndex.Position = 0;

            DX10.BufferDescription bufferDescription = new DX10.BufferDescription();
            bufferDescription.BindFlags = DX10.BindFlags.VertexBuffer;
            bufferDescription.CpuAccessFlags = DX10.CpuAccessFlags.None;
            bufferDescription.OptionFlags = DX10.ResourceOptionFlags.None;
            bufferDescription.SizeInBytes = vertices.Length * Marshal.SizeOf(typeof(Vertex));
            bufferDescription.Usage = DX10.ResourceUsage.Default;

            DX10.BufferDescription bufferDescriptionIndex = new DX10.BufferDescription();
            bufferDescriptionIndex.BindFlags = DX10.BindFlags.IndexBuffer;
            bufferDescriptionIndex.CpuAccessFlags = DX10.CpuAccessFlags.None;
            bufferDescriptionIndex.OptionFlags = DX10.ResourceOptionFlags.None;
            bufferDescriptionIndex.SizeInBytes = indices.Length * Marshal.SizeOf(typeof(short));
            bufferDescriptionIndex.Usage = DX10.ResourceUsage.Default;

            m_vertexBuffer = new DX10.Buffer(device, outStream, bufferDescription);
            m_indexBuffer = new DX10.Buffer(device, outStreamIndex, bufferDescriptionIndex);
            outStream.Close();
            outStreamIndex.Close();

            m_effect = DX10.Effect.FromFile(device, "Shaders/SimpleRendering.fx", "fx_4_0", D3DC.ShaderFlags.None, D3DC.EffectFlags.None);
            m_effectTechnique = m_effect.GetTechniqueByIndex(0);
            m_effectPass = m_effectTechnique.GetPassByIndex(0);
            m_transformVariable = m_effect.GetVariableByName("WorldViewProj").AsMatrix();

            DX10.InputElement[] inputElements = new DX10.InputElement[]
            {
                new DX10.InputElement("POSITION",0,SlimDX.DXGI.Format.R32G32B32_Float,0,0),
                new DX10.InputElement("COLOR",0,SlimDX.DXGI.Format.R8G8B8A8_UNorm,12,0)
            };

            m_vertexLayout = new DX10.InputLayout(device, inputElements, m_effectPass.Description.Signature);
        }
        public void LoadResources(DX10.Device device, string path)
        {
            loadMesh(path+"\\e.elem", path+"\\n.nodes");

            DataStream outStream = new DataStream(vertices.Length * Marshal.SizeOf(typeof(Vertex)), true, true);
            DataStream outStreamIndex = new DataStream(indices.Length * Marshal.SizeOf(typeof(short)), true, true);

            for (int loop = 0; loop < vertices.Length; loop++)
            {
                outStream.Write<Vertex>(vertices[loop]);
            }

            for (int loop = 0; loop < indices.Length; loop++)
            {
                outStreamIndex.Write<short>(indices[loop]);
            }

            outStream.Position = 0;
            outStreamIndex.Position = 0;

            DX10.BufferDescription bufferDescription = new DX10.BufferDescription();
            bufferDescription.BindFlags = DX10.BindFlags.VertexBuffer;
            bufferDescription.CpuAccessFlags = DX10.CpuAccessFlags.None;
            bufferDescription.OptionFlags = DX10.ResourceOptionFlags.None;
            bufferDescription.SizeInBytes = vertices.Length * Marshal.SizeOf(typeof(Vertex));
            bufferDescription.Usage = DX10.ResourceUsage.Default;

            DX10.BufferDescription bufferDescriptionIndex = new DX10.BufferDescription();
            bufferDescriptionIndex.BindFlags = DX10.BindFlags.IndexBuffer;
            bufferDescriptionIndex.CpuAccessFlags = DX10.CpuAccessFlags.None;
            bufferDescriptionIndex.OptionFlags = DX10.ResourceOptionFlags.None;
            bufferDescriptionIndex.SizeInBytes = indices.Length * Marshal.SizeOf(typeof(short));
            bufferDescriptionIndex.Usage = DX10.ResourceUsage.Default;

            m_vertexBuffer = new DX10.Buffer(device, outStream, bufferDescription);
            m_indexBuffer = new DX10.Buffer(device, outStreamIndex, bufferDescriptionIndex);
            outStream.Close();
            outStreamIndex.Close();

            m_effect = DX10.Effect.FromFile(device, "Shaders/SimpleRendering.fx", "fx_4_0", D3DC.ShaderFlags.None, D3DC.EffectFlags.None);
            m_effectTechnique = m_effect.GetTechniqueByIndex(0);
            m_effectPass = m_effectTechnique.GetPassByIndex(0);
            m_transformVariable = m_effect.GetVariableByName("WorldViewProj").AsMatrix();

            DX10.InputElement[] inputElements = new DX10.InputElement[]
            {
                new DX10.InputElement("POSITION",0,SlimDX.DXGI.Format.R32G32B32_Float,0,0),
                new DX10.InputElement("COLOR",0,SlimDX.DXGI.Format.R8G8B8A8_UNorm,12,0)
              // new DX10.InputElement("COLOR",0,SlimDX.DXGI.Format.R8G8B8A8_UInt,12,0)
            };

            m_vertexLayout = new DX10.InputLayout(device, inputElements, m_effectPass.Description.Signature);
        }

        public void Render(DX10.Device device, Matrix world, Matrix viewProj)
        {
            device.InputAssembler.SetInputLayout(m_vertexLayout);
            device.InputAssembler.SetPrimitiveTopology(DX10.PrimitiveTopology.TriangleList);
            device.InputAssembler.SetIndexBuffer(m_indexBuffer, DXGI.Format.R16_UInt, 0);
            device.InputAssembler.SetVertexBuffers(0, new DX10.VertexBufferBinding(m_vertexBuffer, Marshal.SizeOf(typeof(Vertex)), 0));

            m_transformVariable.SetMatrix(world * viewProj);

            m_effectPass.Apply();
            device.DrawIndexed(indices.Length, 0, 0);
        }
    }
}

